using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace GuavaData
{
   
    public class ClassSchema
    {
        public List<Column> columns;
        public string name;
        public string comments;

        public ClassSchema(IEnumerable<Column> colsIn, string nameIn, string commentsIn)
        {
            columns = new List<Column>(colsIn);
            name = nameIn;
            comments = commentsIn;
        }

        public XmlDocument ToXml()
        {
            XmlDocument xd = new XmlDocument();
            XmlNode xdec = xd.CreateNode(XmlNodeType.XmlDeclaration, "", "");
            xd.AppendChild(xdec);

            XmlElement xroot = xd.CreateElement("guava:Schema");
            xd.AppendChild(xroot);

            // Create name node
            XmlElement xsname = xd.CreateElement("guava:SchemaName");
            XmlText xsnametext = xd.CreateTextNode(name);
            xsname.AppendChild(xsnametext);
            xroot.AppendChild(xsname);
            
            // Create columns collection
            XmlElement xcols = xd.CreateElement("guava:ColumnCollection");
            xroot.AppendChild(xcols);

            // Add columns to collection
            foreach (Column c in columns)
            {
                XmlElement xcol = xd.CreateElement("guava:Column");
                XmlElement xcname = xd.CreateElement("guava:ColumnName");
                XmlText xcnametext = xd.CreateTextNode(c.Name);
                xcname.AppendChild(xcnametext);
                xcol.AppendChild(xcname);
                xcol.AppendChild(c.dom.ToXml(xd));
                xcols.AppendChild(xcol);
            }

            // Create comments node
            XmlElement xcomm = xd.CreateElement("guava:SchemaComments");
            XmlText xcommtext = xd.CreateTextNode(comments);
            xcomm.AppendChild(xcommtext);
            xroot.AppendChild(xcomm);

            return xd;
        }

        public static ClassSchema ParseXml(XmlDocument xd)
        {
            string name = xd["Schema"]["SchemaName"].InnerText;
            string comments = xd["Schema"]["SchemaComments"].InnerText;
            List<Column> lc = new List<Column>();

            foreach (XmlElement xn in xd["Schema"]["ColumnCollection"].ChildNodes)
            {
                string cname = xn["ColumnName"].InnerText;
                XmlElement xdom = xn["Domain"];
                string cdom = xdom.GetAttribute("Type");

                if (cdom == "Boolean")
                    lc.Add(new Column(cname, new GuavaData.Domain.Boolean()));

                else if (cdom == "Enumerated")
                {
                    GuavaData.Domain.Enumerated e = new GuavaData.Domain.Enumerated();
                    foreach (XmlElement item in xdom.ChildNodes) e.Items.Add(item.InnerText);
                    lc.Add(new Column(cname, e));
                }

                else if (cdom == "String")
                {
                    if (xdom.HasAttribute("Width")) lc.Add(new Column(cname, new GuavaData.Domain.String(int.Parse(xdom.GetAttribute("Width")))));
                    else lc.Add(new Column(cname, new GuavaData.Domain.String()));
                }

                else if (cdom == "Lookup")
                    lc.Add(new Column(cname, new GuavaData.Domain.Lookup(xdom.GetAttribute("TableName"), xdom.GetAttribute("View"))));

                else if (cdom == "UniqueIdentifier")
                    lc.Add(new Column(cname, new GuavaData.Domain.UniqueIdentifier()));

                else if (cdom == "DateTime")
                    lc.Add(new Column(cname, new GuavaData.Domain.DateTime()));

                else if (cdom == "BigInteger")
                {
                    long? max;
                    if (xdom.HasAttribute("MaxValue")) max = Int64.Parse(xdom.GetAttribute("MaxValue"));
                    else max = null;

                    long? min;
                    if (xdom.HasAttribute("MinValue")) min = Int64.Parse(xdom.GetAttribute("MinValue"));
                    else min = null;

                    lc.Add(new Column(cname, new GuavaData.Domain.BigInteger(min, max)));
                }

                else if (cdom == "Integer")
                {
                    int? max;
                    if (xdom.HasAttribute("MaxValue")) max = Int32.Parse(xdom.GetAttribute("MaxValue"));
                    else max = null;

                    int? min;
                    if (xdom.HasAttribute("MinValue")) min = Int32.Parse(xdom.GetAttribute("MinValue"));
                    else min = null;

                    lc.Add(new Column(cname, new GuavaData.Domain.Integer(min, max)));
                }

                else if (cdom == "SmallInteger")
                {
                    short? max;
                    if (xdom.HasAttribute("MaxValue")) max = Int16.Parse(xdom.GetAttribute("MaxValue"));
                    else max = null;

                    short? min;
                    if (xdom.HasAttribute("MinValue")) min = Int16.Parse(xdom.GetAttribute("MinValue"));
                    else min = null;

                    lc.Add(new Column(cname, new GuavaData.Domain.SmallInteger(min, max)));
                }

                else if (cdom == "TinyInteger")
                {
                    byte? max;
                    if (xdom.HasAttribute("MaxValue")) max = Byte.Parse(xdom.GetAttribute("MaxValue"));
                    else max = null;

                    byte? min;
                    if (xdom.HasAttribute("MinValue")) min = Byte.Parse(xdom.GetAttribute("MinValue"));
                    else min = null;

                    lc.Add(new Column(cname, new GuavaData.Domain.TinyInteger(min, max)));
                }

                else if (cdom == "Decimal")
                {
                    decimal? max;
                    if (xdom.HasAttribute("MaxValue")) max = Decimal.Parse(xdom.GetAttribute("MaxValue"));
                    else max = null;

                    decimal? min;
                    if (xdom.HasAttribute("MinValue")) min = Decimal.Parse(xdom.GetAttribute("MinValue"));
                    else min = null;

                    lc.Add(new Column(cname, new GuavaData.Domain.Decimal(min, max)));
                }

                else if (cdom == "Single")
                {
                    float? max;
                    if (xdom.HasAttribute("MaxValue")) max = Single.Parse(xdom.GetAttribute("MaxValue"));
                    else max = null;

                    float? min;
                    if (xdom.HasAttribute("MinValue")) min = Single.Parse(xdom.GetAttribute("MinValue"));
                    else min = null;

                    lc.Add(new Column(cname, new GuavaData.Domain.Single(min, max)));
                }

                else if (cdom == "Double")
                {
                    double? max;
                    if (xdom.HasAttribute("MaxValue")) max = Double.Parse(xdom.GetAttribute("MaxValue"));
                    else max = null;

                    double? min;
                    if (xdom.HasAttribute("MinValue")) min = Double.Parse(xdom.GetAttribute("MinValue"));
                    else min = null;

                    lc.Add(new Column(cname, new GuavaData.Domain.Double(min, max)));
                }
            }

            return new ClassSchema(lc, name, comments);
        }
    }
}
